#include "StdAfx.hpp"
#include "Data.hpp"
#include "Context.hpp"

static const bool ALLOW_NUMBER_BOOL_CONVERSION = true;

class BreakException { };
class ContinueException { };
class ReturnException
{
public:
	ValuePtr Val;

	ReturnException() { }
	ReturnException(ValuePtr val) : Val(val) { }
};

static void PrintDebugIndent(uint level)
{
	for (uint i = 0; i < level; i++)
		std::cout << "  ";
}

static void EnsureExpressionType(ExpressionPtr &expr, const Type &desiredType, PreprocessContext &ctx)
{
	assert(!expr->ExprType.IsEmpty());
	if (expr->ExprType == desiredType)
		return;
	TypeConversion *convExpr = new TypeConversion(expr, desiredType, false);
	expr.reset(convExpr);
	ExpressionPtr newExpr = convExpr->PreprocessConversion(ctx);
	if (!newExpr.is_null())
		expr.reset(newExpr);
}

void PreprocessExpressionNode(ExpressionPtr &expr, PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	ExpressionPtr newExpr = expr->Preprocess(ctx, typeSuggestion);
	if (!newExpr.is_null())
		expr = newExpr;
}

void PreprocessStatementNode(StatementPtr &stmt, PreprocessContext &ctx)
{
	StatementPtr newStmt = stmt->Preprocess(ctx);
	if (!newStmt.is_null())
		stmt = newStmt;
}

const char * UnaryOperator::OP_TYPE_NAMES[] = {
	"Numeric negation",
	"Binary negation",
	"Logical negation",
	"Preincrementation",
	"Predecrementation",
	"Postincrementation",
	"Postdecrementation",
	"Sizeof",
};

ExpressionPtr UnaryOperator::Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	switch (OpType)
	{
	case OP_NUM_NEGATION:
		{
			TypeSuggestion newSuggestion = (typeSuggestion.IsEmpty() || typeSuggestion.T.GetType() == Type::TYPE_VOID)
				? TypeSuggestion(Type(), Type::CLASS_NUMERIC | Type::CLASS_SIGNED)
				: typeSuggestion;
			PreprocessExpressionNode(SubExpr, ctx, newSuggestion);
			ExprType = SubExpr->ExprType;
			if (!ExprType.CheckClass(Type::CLASS_NUMERIC))
				throw Error("Numeric negation operator requires expression of numeric type");
			if (ExprType.CheckClass(Type::CLASS_UNSIGNED))
				std::cout << "Warning: Unary minus operator applied to unsigned type, result still unsigned" << std::endl;
		}
		break;

	case OP_BIN_NEGATION:
		{
			TypeSuggestion newSuggestion = (typeSuggestion.IsEmpty() || typeSuggestion.T.GetType() == Type::TYPE_VOID)
				? TypeSuggestion(Type(), Type::CLASS_INTEGER)
				: typeSuggestion;
			PreprocessExpressionNode(SubExpr, ctx, newSuggestion);
			if (!SubExpr->ExprType.CheckClass(Type::CLASS_INTEGER))
				throw Error("Binary negation uperator requires expression of integer type");
			ExprType = SubExpr->ExprType;
		}
		break;

	case OP_LOG_NEGATION:
		PreprocessExpressionNode(SubExpr, ctx, TypeSuggestion(Type(Type::TYPE_BOOL), 0));
		ExprType = Type(Type::TYPE_BOOL);
		EnsureExpressionType(SubExpr, ExprType, ctx);
		break;

	case OP_INC_PRE:
	case OP_DEC_PRE:
	case OP_INC_POST:
	case OP_DEC_POST:
		{
			TypeSuggestion newSuggestion = (typeSuggestion.IsEmpty() || typeSuggestion.T.GetType() == Type::TYPE_VOID)
				? TypeSuggestion(Type(), Type::CLASS_INTEGER)
				: typeSuggestion;
			PreprocessExpressionNode(SubExpr, ctx, newSuggestion);
			ExprType = SubExpr->ExprType;
			if (!ExprType.CheckClass(Type::CLASS_INTEGER))
				throw Error("This operator requires expression of integer type");
			if (!SubExpr->IsLvalue())
				throw Error("This operator requires L-value expression");
		}
		break;

	case OP_SIZEOF:
		PreprocessExpressionNode(SubExpr, ctx, TypeSuggestion());
		ExprType = Type(Type::TYPE_UINT32);
		if (!SubExpr->ExprType.CheckClass(Type::CLASS_PHYSICAL))
			throw Error("Cannot get size of type " + SubExpr->ExprType.GetName_r());
		break;

	default:
		assert(0);
	}

	return ExpressionPtr();
}

template <typename T>
static void BinaryNegation(void *out, Expression *expr)
{
	expr->Eval(out);
	*(T*)out = ~ *(T*)out;
}

#pragma warning(push)
#pragma warning(disable:4146)
template <typename T>
static void NumericNegation(void *out, Expression *expr)
{
	expr->Eval(out);
	*(T*)out = - *(T*)out;
}
#pragma warning(pop)

void UnaryOperator::Eval_BinNegation(void *out)
{
	switch (ExprType.GetSize())
	{
	case 1: BinaryNegation<uint1>(out, SubExpr.get()); break;
	case 2: BinaryNegation<uint2>(out, SubExpr.get()); break;
	case 4: BinaryNegation<uint4>(out, SubExpr.get()); break;
	case 8: BinaryNegation<uint8>(out, SubExpr.get()); break;
	default: assert(0);
	}
}

void UnaryOperator::Eval_NumNegation(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  NumericNegation<uint1>(out, SubExpr.get()); break;
	case Type::TYPE_UINT16: NumericNegation<uint2>(out, SubExpr.get()); break;
	case Type::TYPE_UINT32: NumericNegation<uint4>(out, SubExpr.get()); break;
	case Type::TYPE_UINT64: NumericNegation<uint8>(out, SubExpr.get()); break;
	case Type::TYPE_INT8:   NumericNegation<int1>(out, SubExpr.get()); break;
	case Type::TYPE_INT16:  NumericNegation<int2>(out, SubExpr.get()); break;
	case Type::TYPE_INT32:  NumericNegation<int4>(out, SubExpr.get()); break;
	case Type::TYPE_INT64:  NumericNegation<int8>(out, SubExpr.get()); break;
	case Type::TYPE_FLOAT:  NumericNegation<float>(out, SubExpr.get()); break;
	case Type::TYPE_DOUBLE: NumericNegation<double>(out, SubExpr.get()); break;
	default: assert(0);
	}
}

template <typename T>
void UnaryOperator::Eval_IncDec_T(void *out)
{
	T *lval = (T*)SubExpr->AccessLvalue();
	T *outVal = (T*)out;

	switch (OpType)
	{
	case OP_INC_PRE:  *outVal = ++(*lval); break;
	case OP_DEC_PRE:  *outVal = --(*lval); break;
	case OP_INC_POST: *outVal = (*lval)++; break;
	case OP_DEC_POST: *outVal = (*lval)--; break;
	default: assert(0);
	}
}

void UnaryOperator::Eval_IncDec(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_IncDec_T<uint1>(out); break;
	case Type::TYPE_UINT16: Eval_IncDec_T<uint2>(out); break;
	case Type::TYPE_UINT32: Eval_IncDec_T<uint4>(out); break;
	case Type::TYPE_UINT64: Eval_IncDec_T<uint8>(out); break;
	case Type::TYPE_INT8:   Eval_IncDec_T<int1>(out); break;
	case Type::TYPE_INT16:  Eval_IncDec_T<int2>(out); break;
	case Type::TYPE_INT32:  Eval_IncDec_T<int4>(out); break;
	case Type::TYPE_INT64:  Eval_IncDec_T<int8>(out); break;
	case Type::TYPE_FLOAT:  Eval_IncDec_T<float>(out); break;
	case Type::TYPE_DOUBLE: Eval_IncDec_T<double>(out); break;
	}
}

void UnaryOperator::Eval(void *out)
{
	switch (OpType)
	{
	case OP_NUM_NEGATION:
		Eval_NumNegation(out);
		break;
	case OP_BIN_NEGATION:
		Eval_BinNegation(out);
		break;
	case OP_LOG_NEGATION:
		{
			SubExpr->Eval(out);
			bool *val = (bool*)out;
			*val = ! *val;
		}
		break;
	case OP_INC_PRE:
	case OP_DEC_PRE:
	case OP_INC_POST:
	case OP_DEC_POST:
		Eval_IncDec(out);
		break;
	case OP_SIZEOF:
		*(uint*)out = SubExpr->ExprType.GetSize();
		break;
	default:
		assert(0);
	}
}

ExpressionPtr TypeConversion::Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	PreprocessExpressionNode(SubExpr, ctx, TypeSuggestion(ExprType, 0));
	return PreprocessConversion(ctx);
}

template <typename T>
static void Convert_AnyToBool(bool *outVal, Expression *expr)
{
	T srcVal;
	expr->Eval(&srcVal);
	*outVal = srcVal != T();
}

template <typename T>
static void Convert_NumericToChar(char *outVal, Expression *expr)
{
	T srcVal;
	expr->Eval(&srcVal);
	*outVal = (char)(unsigned char)srcVal;
}

template <typename T>
static void Convert_AnyToString(string *outVal, Expression *expr)
{
	T srcVal;
	expr->Eval(&srcVal);
	SthToStr<T>(outVal, srcVal);
}

template <typename T>
static void Convert_UintToString(string *outVal, Expression *expr)
{
	T srcVal;
	expr->Eval(&srcVal);
	UintToStr<T>(outVal, srcVal);
}

template <typename T>
static void Convert_IntToString(string *outVal, Expression *expr)
{
	T srcVal;
	expr->Eval(&srcVal);
	IntToStr<T>(outVal, srcVal);
}

template <typename T>
static void Convert_BoolToNumeric(void *out, Expression *Expr)
{
	bool srcVal;
	Expr->Eval(&srcVal);
	*(T*)out = srcVal ? (T)1 : T();
}

template <typename T>
static void Convert_CharToNumeric(void *out, Expression *Expr)
{
	char srcVal;
	Expr->Eval(&srcVal);
	*(T*)out = (T)(unsigned char)srcVal;
}

template <typename T>
static void Convert_StringToAny(void *out, Expression *Expr, const Type &type)
{
	string srcVal;
	Expr->Eval(&srcVal);
	if (!StrToSth<T>((T*)out, srcVal))
		throw Error("Cannot convert value of type "+type.GetName_r()+" to string");
}

template <typename dstT, typename srcT>
static void Convert_NumericToNumeric(void *out, Expression *Expr, const Type &type)
{
	srcT srcVal;
	Expr->Eval(&srcVal);
	*(dstT*)out = (dstT)srcVal;
}

template <typename dstT>
static void Convert_NumericToNumeric_DstT(void *out, Expression *Expr, const Type &type)
{
	switch (Expr->ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Convert_NumericToNumeric<dstT, uint1>(out, Expr, type); return;
	case Type::TYPE_UINT16: Convert_NumericToNumeric<dstT, uint2>(out, Expr, type); return;
	case Type::TYPE_UINT32: Convert_NumericToNumeric<dstT, uint4>(out, Expr, type); return;
	case Type::TYPE_UINT64: Convert_NumericToNumeric<dstT, uint8>(out, Expr, type); return;
	case Type::TYPE_INT8:  Convert_NumericToNumeric<dstT, int1>(out, Expr, type); return;
	case Type::TYPE_INT16: Convert_NumericToNumeric<dstT, int2>(out, Expr, type); return;
	case Type::TYPE_INT32: Convert_NumericToNumeric<dstT, int4>(out, Expr, type); return;
	case Type::TYPE_INT64: Convert_NumericToNumeric<dstT, int8>(out, Expr, type); return;
	case Type::TYPE_FLOAT: Convert_NumericToNumeric<dstT, float>(out, Expr, type); return;
	case Type::TYPE_DOUBLE: Convert_NumericToNumeric<dstT, double>(out, Expr, type); return;
	default: assert(0);
	}
}

void TypeConversion::Eval(void *out)
{
	// Rules here are equvalent to Type::CheckConvFrom.

	// No conversion at all
	if (SubExpr->ExprType == ExprType)
	{
		SubExpr->Eval(out);
		return;
	}

	// Casting to void - discarding value
	if (ExprType.GetType() == Type::TYPE_VOID)
	{
		assert(out == NULL);
		Value val(SubExpr->ExprType);
		SubExpr->Eval(val.GetAddr());
		return;
	}

	// Any to bool
	if (ExprType.GetType() == Type::TYPE_BOOL)
	{
		bool *outVal = (bool*)out;
		switch (SubExpr->ExprType.GetType())
		{
		case Type::TYPE_CHAR:   Convert_AnyToBool<char>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT8:  Convert_AnyToBool<uint1>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT16: Convert_AnyToBool<uint2>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT32: Convert_AnyToBool<uint4>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT64: Convert_AnyToBool<uint8>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT8:   Convert_AnyToBool<int1>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT16:  Convert_AnyToBool<int2>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT32:  Convert_AnyToBool<int4>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT64:  Convert_AnyToBool<int8>(outVal, SubExpr.get()); return;
		case Type::TYPE_FLOAT:  Convert_AnyToBool<float>(outVal, SubExpr.get()); return;
		case Type::TYPE_DOUBLE: Convert_AnyToBool<double>(outVal, SubExpr.get()); return;
		case Type::TYPE_STRING:
			{
				string srcVal;
				SubExpr->Eval(&srcVal);
				*outVal = !srcVal.empty();
				return;
			}
		}
	}
	// Conversion to char
	if (ExprType.GetType() == Type::TYPE_CHAR)
	{
		char *outVal = (char*)out;
		switch (SubExpr->ExprType.GetType())
		{
		case Type::TYPE_STRING:
			{
				string srcVal;
				SubExpr->Eval(&srcVal);
				if (srcVal.length() != 1)
					throw Error("Cannot convert string to char: Invalid string length.");
				*outVal = srcVal[0];
				return;
			}
		case Type::TYPE_UINT8:  Convert_NumericToChar<uint1>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT16: Convert_NumericToChar<uint2>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT32: Convert_NumericToChar<uint4>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT64: Convert_NumericToChar<uint8>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT8:   Convert_NumericToChar<int1>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT16:  Convert_NumericToChar<int2>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT32:  Convert_NumericToChar<int4>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT64:  Convert_NumericToChar<int8>(outVal, SubExpr.get()); return;
		case Type::TYPE_FLOAT:  Convert_NumericToChar<float>(outVal, SubExpr.get()); return;
		case Type::TYPE_DOUBLE: Convert_NumericToChar<double>(outVal, SubExpr.get()); return;
		case Type::TYPE_BOOL:
			{
				bool srcVal;
				SubExpr->Eval(&srcVal);
				*outVal = srcVal ? 1 : 0;
				return;
			}
		}
	}

	// Conversion to string
	if (ExprType.GetType() == Type::TYPE_STRING)
	{
		string *outVal = (string*)out;
		switch (SubExpr->ExprType.GetType())
		{
		case Type::TYPE_CHAR:   Convert_AnyToString<char>(outVal, SubExpr.get()); return;
		case Type::TYPE_BOOL:   Convert_AnyToString<bool>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT8:  Convert_UintToString<uint1>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT16: Convert_UintToString<uint2>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT32: Convert_UintToString<uint4>(outVal, SubExpr.get()); return;
		case Type::TYPE_UINT64: Convert_UintToString<uint8>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT8:   Convert_IntToString<int1>(outVal, SubExpr.get()); return; // Warning! Using SthToStr whould treat it as char!
		case Type::TYPE_INT16:  Convert_IntToString<int2>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT32:  Convert_IntToString<int4>(outVal, SubExpr.get()); return;
		case Type::TYPE_INT64:  Convert_IntToString<int8>(outVal, SubExpr.get()); return;
		case Type::TYPE_FLOAT:  Convert_AnyToString<float>(outVal, SubExpr.get()); return;
		case Type::TYPE_DOUBLE: Convert_AnyToString<double>(outVal, SubExpr.get()); return;
		}
	}

	// Bool to numeric
	if (SubExpr->ExprType.GetType() == Type::TYPE_BOOL)
	{
		switch (ExprType.GetType())
		{
		case Type::TYPE_UINT8:  Convert_BoolToNumeric<uint1>(out, SubExpr.get()); return;
		case Type::TYPE_UINT16: Convert_BoolToNumeric<uint2>(out, SubExpr.get()); return;
		case Type::TYPE_UINT32: Convert_BoolToNumeric<uint4>(out, SubExpr.get()); return;
		case Type::TYPE_UINT64: Convert_BoolToNumeric<uint8>(out, SubExpr.get()); return;
		case Type::TYPE_INT8:   Convert_BoolToNumeric<int1>(out, SubExpr.get()); return;
		case Type::TYPE_INT16:  Convert_BoolToNumeric<int2>(out, SubExpr.get()); return;
		case Type::TYPE_INT32:  Convert_BoolToNumeric<int4>(out, SubExpr.get()); return;
		case Type::TYPE_INT64:  Convert_BoolToNumeric<int8>(out, SubExpr.get()); return;
		case Type::TYPE_FLOAT:  Convert_BoolToNumeric<float>(out, SubExpr.get()); return;
		case Type::TYPE_DOUBLE: Convert_BoolToNumeric<double>(out, SubExpr.get()); return;
		}
	}

	// Char to numeric
	if (SubExpr->ExprType.GetType() == Type::TYPE_CHAR)
	{
		switch (ExprType.GetType())
		{
		case Type::TYPE_UINT8:  Convert_CharToNumeric<uint1>(out, SubExpr.get()); return;
		case Type::TYPE_UINT16: Convert_CharToNumeric<uint2>(out, SubExpr.get()); return;
		case Type::TYPE_UINT32: Convert_CharToNumeric<uint4>(out, SubExpr.get()); return;
		case Type::TYPE_UINT64: Convert_CharToNumeric<uint8>(out, SubExpr.get()); return;
		case Type::TYPE_INT8:   Convert_CharToNumeric<int1>(out, SubExpr.get()); return;
		case Type::TYPE_INT16:  Convert_CharToNumeric<int2>(out, SubExpr.get()); return;
		case Type::TYPE_INT32:  Convert_CharToNumeric<int4>(out, SubExpr.get()); return;
		case Type::TYPE_INT64:  Convert_CharToNumeric<int8>(out, SubExpr.get()); return;
		case Type::TYPE_FLOAT:  Convert_CharToNumeric<float>(out, SubExpr.get()); return;
		case Type::TYPE_DOUBLE: Convert_CharToNumeric<double>(out, SubExpr.get()); return;
		}
	}

	// String to anyting (parsing)
	if (SubExpr->ExprType.GetType() == Type::TYPE_STRING)
	{
		switch (ExprType.GetType())
		{
		case Type::TYPE_UINT8:  Convert_StringToAny<uint1>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_UINT16: Convert_StringToAny<uint2>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_UINT32: Convert_StringToAny<uint4>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_UINT64: Convert_StringToAny<uint8>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT8:   Convert_StringToAny<int1>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT16:  Convert_StringToAny<int2>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT32:  Convert_StringToAny<int4>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT64:  Convert_StringToAny<int8>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_FLOAT:  Convert_StringToAny<float>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_DOUBLE: Convert_StringToAny<double>(out, SubExpr.get(), ExprType); return;
		// TYPE_CHAR already processed before.
		}
	}

	// Between numeric types
	if (ExprType.CheckClass(Type::CLASS_NUMERIC) && SubExpr->ExprType.CheckClass(Type::CLASS_NUMERIC))
	{
		switch (ExprType.GetType())
		{
		case Type::TYPE_UINT8:  Convert_NumericToNumeric_DstT<uint1>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_UINT16: Convert_NumericToNumeric_DstT<uint2>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_UINT32: Convert_NumericToNumeric_DstT<uint4>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_UINT64: Convert_NumericToNumeric_DstT<uint8>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT8:  Convert_NumericToNumeric_DstT<int1>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT16: Convert_NumericToNumeric_DstT<int2>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT32: Convert_NumericToNumeric_DstT<int4>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_INT64: Convert_NumericToNumeric_DstT<int8>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_FLOAT: Convert_NumericToNumeric_DstT<float>(out, SubExpr.get(), ExprType); return;
		case Type::TYPE_DOUBLE: Convert_NumericToNumeric_DstT<double>(out, SubExpr.get(), ExprType); return;
		}
	}

	assert(0 && "Invalid type conversion.");
}

ExpressionPtr TypeConversion::PreprocessConversion( PreprocessContext &ctx )
{
	TypeConv conv = ExprType.CheckConvFrom(SubExpr->ExprType, ctx.Rules);
	
	if (conv == TypeConv_None
		|| !Explicit && conv == TypeConv_Explicit)
	{
		throw Error("Cannot convert from "+SubExpr->ExprType.GetName_r()+" to "+ExprType.GetName_r());
	}
	
	if (!Explicit && conv == TypeConv_Implicit_Warning)
	{
		/*bool subExprIsExplicitConstant = false;
		if (typeid(*SubExpr.get()) == typeid(Constant))
			subExprIsExplicitConstant = ((Constant*)SubExpr.get())->Explicit;
		if (!subExprIsExplicitConstant)*/
			// TODO - warning system
			std::cout << "Warning: Conversion from " << SubExpr->ExprType.GetName_r() << " to " << ExprType.GetName_r() << std::endl;
	}

	// this->ExprType already filled in constructor.

	return ExpressionPtr();
}

const char * BinaryOperator::OP_TYPE_NAMES[] = {
	"Numeric addition",
	"Numeric subtraction",
	"Numeric multiplication",
	"Numeric division",
	"Numeric modulo",
	"Binary and",
	"Binary or",
	"Binary xor",
	"Binary shift left",
	"Binary shift right",
	"Logical and",
	"Logical or",
	"Equal",
	"Not equal",
	"Less",
	"Greater",
	"Less or equal",
	"Greater or equal",
	"String concatenation",
	"Return left",
	"Assignment",
	"Numeric addition in place",
	"Numeric subtraction in place",
	"Numeric multiplication in place",
	"Numeric division in place",
	"Numeric modulo in place",
	"String concatenation in place",
	"Binary and in place",
	"Binary or in place",
	"Binary xor in place",
	"Binary shift left in place",
	"Binary shift right in place",
};

const char * BinaryOperator::OP_TYPE_SYMBOLS[] = {
	"+", "-", "*", "/", "%",
	"&", "|", "^",
	"<<", ">>",
	"&&", "||",
	"==", "!=", "<", ">", "<=", ">=",
	NULL, NULL,
	"=", "+=", "-=", "*=", "/=", "%=", NULL, "&=", "|=", "^=", "<<=", ">>=",
};

ExpressionPtr BinaryOperator::Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	switch (OpType)
	{
	case OP_NUM_ADDITION:
		{
			TypeSuggestion newSuggestion = (typeSuggestion.IsEmpty() || typeSuggestion.T.GetType() == Type::TYPE_VOID)
				? TypeSuggestion(Type(), Type::CLASS_NUMERIC)
				: typeSuggestion;
			PreprocessExpressionNode(SubExpr1, ctx, newSuggestion);
			PreprocessExpressionNode(SubExpr2, ctx, newSuggestion);
			SubExprType.Promote(SubExpr1->ExprType, SubExpr2->ExprType);
			if (SubExprType == Type(Type::TYPE_STRING) || SubExprType == Type(Type::TYPE_CHAR))
			{
				OpType = OP_STR_CONCAT;
				ExprType.Set(Type::TYPE_STRING);
				if (SubExpr1->ExprType != Type(Type::TYPE_CHAR))
					EnsureExpressionType(SubExpr1, Type(Type::TYPE_STRING), ctx);
				if (SubExpr2->ExprType != Type(Type::TYPE_CHAR))
					EnsureExpressionType(SubExpr2, Type(Type::TYPE_STRING), ctx);
			}
			else
			{
				ExprType = SubExprType;
				if (!ExprType.CheckClass(Type::CLASS_NUMERIC))
					throw Error("Operator requires expression of numeric type");
				EnsureExpressionType(SubExpr1, SubExprType, ctx);
				EnsureExpressionType(SubExpr2, SubExprType, ctx);
			}
		}
		break;

	case OP_NUM_SUBTRACTION:
	case OP_NUM_MULTIPLICATION:
	case OP_NUM_DIVISION:
		{
			TypeSuggestion newSuggestion = (typeSuggestion.IsEmpty() || typeSuggestion.T.GetType() == Type::TYPE_VOID)
				? TypeSuggestion(Type(), Type::CLASS_NUMERIC)
				: typeSuggestion;
			PreprocessExpressionNode(SubExpr1, ctx, newSuggestion);
			PreprocessExpressionNode(SubExpr2, ctx, newSuggestion);
			SubExprType.Promote(SubExpr1->ExprType, SubExpr2->ExprType);
			ExprType = SubExprType;
			if (!ExprType.CheckClass(Type::CLASS_NUMERIC))
				throw Error("Operator requires expression of numeric type");
			EnsureExpressionType(SubExpr1, SubExprType, ctx);
			EnsureExpressionType(SubExpr2, SubExprType, ctx);
		}
		break;

	case OP_NUM_MODULO:
	case OP_BIN_AND:
	case OP_BIN_OR:
	case OP_BIN_XOR:
	case OP_BIN_SHIFT_LEFT:
	case OP_BIN_SHIFT_RIGHT:
		{
			TypeSuggestion newSuggestion = (typeSuggestion.IsEmpty() || typeSuggestion.T.GetType() == Type::TYPE_VOID)
				? TypeSuggestion(Type(), Type::CLASS_INTEGER | Type::CLASS_UNSIGNED)
				: typeSuggestion;
			PreprocessExpressionNode(SubExpr1, ctx, newSuggestion);
			PreprocessExpressionNode(SubExpr2, ctx, newSuggestion);
			ExprType.Promote(SubExpr1->ExprType, SubExpr2->ExprType);
			if (!ExprType.CheckClass(Type::CLASS_INTEGER))
				throw Error("This operator requires expression of integer type");
			EnsureExpressionType(SubExpr1, ExprType, ctx);
			EnsureExpressionType(SubExpr2, ExprType, ctx);
		}
		break;

	case OP_CMP_EQUAL:
	case OP_CMP_NOT_EQUAL:
		PreprocessExpressionNode(SubExpr1, ctx, TypeSuggestion()); // Suggest equatable some way?
		PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(SubExpr1->ExprType, 0));
		ExprType.Set(Type::TYPE_BOOL);
		SubExprType.Promote(SubExpr1->ExprType, SubExpr2->ExprType);
		if (!SubExprType.IsEquatable())
			throw Error("Type " + SubExprType.GetName_r() + " is not equatable");
		EnsureExpressionType(SubExpr1, SubExprType, ctx);
		EnsureExpressionType(SubExpr2, SubExprType, ctx);
		break;

	case OP_CMP_LESS:
	case OP_CMP_GREATER:
	case OP_CMP_LESS_EQUAL:
	case OP_CMP_GREATER_EQUAL:
		PreprocessExpressionNode(SubExpr1, ctx, TypeSuggestion()); // Suggest comparable some way?
		PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(SubExpr1->ExprType, 0));
		ExprType.Set(Type::TYPE_BOOL);
		SubExprType.Promote(SubExpr1->ExprType, SubExpr2->ExprType);
		if (!TypeIsComparable(SubExprType))
			throw Error("Type " + SubExprType.GetName_r() + " is not comparable");
		EnsureExpressionType(SubExpr1, SubExprType, ctx);
		EnsureExpressionType(SubExpr2, SubExprType, ctx);
		break;

	case OP_LOG_AND:
	case OP_LOG_OR:
		PreprocessExpressionNode(SubExpr1, ctx, TypeSuggestion(Type(Type::TYPE_BOOL), 0));
		PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(Type(Type::TYPE_BOOL), 0));
		ExprType.Set(Type::TYPE_BOOL);
		EnsureExpressionType(SubExpr1, ExprType, ctx);
		EnsureExpressionType(SubExpr2, ExprType, ctx);
		break;

	case OP_RETURN_LEFT:
		PreprocessExpressionNode(SubExpr1, ctx, typeSuggestion);
		PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(Type(Type::TYPE_VOID), 0));
		ExprType = SubExpr1->ExprType;
		EnsureExpressionType(SubExpr2, Type(Type::TYPE_VOID), ctx);
		break;

	// Assignment operators
	case OP_ASSIGN:
	case OP_ASSIGN_NUM_ADD:
	case OP_ASSIGN_NUM_SUB:
	case OP_ASSIGN_NUM_MUL:
	case OP_ASSIGN_NUM_DIV:
	case OP_ASSIGN_NUM_MOD:
	//case OP_ASSIGN_STR_CONCAT:
	case OP_ASSIGN_BIN_AND:
	case OP_ASSIGN_BIN_OR:
	case OP_ASSIGN_BIN_XOR:
	case OP_ASSIGN_BIN_SHIFT_LEFT:
	case OP_ASSIGN_BIN_SHIFT_RIGHT:
		PreprocessAssignment(ctx, typeSuggestion);
		break;

	default:
		assert(0);
	}

	return ExpressionPtr();
}

template <typename T>
void BinaryOperator::Eval_NumAdd_T(void *out)
{
	SubExpr1->Eval(out);

	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out += rhs;
}

template <typename T>
void BinaryOperator::Eval_NumSub_T(void *out)
{
	SubExpr1->Eval(out);

	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out -= rhs;
}

template <typename T>
void BinaryOperator::Eval_NumMul_T(void *out)
{
	SubExpr1->Eval(out);

	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out *= rhs;
}

template <typename T>
void BinaryOperator::Eval_IntegerDiv_T(void *out)
{
	SubExpr1->Eval(out);

	T rhs;
	SubExpr2->Eval(&rhs);
	if (rhs == T())
		throw Error("Integer division by zero");
	*(T*)out /= rhs;
}

template <typename T>
void BinaryOperator::Eval_IntegerMod_T(void *out)
{
	SubExpr1->Eval(out);

	T rhs;
	SubExpr2->Eval(&rhs);
	if (rhs == T())
		throw Error("Integer modulo division by zero");
	*(T*)out %= rhs;
}

template <typename T>
void BinaryOperator::Eval_RealDiv_T(void *out)
{
	SubExpr1->Eval(out);

	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out /= rhs;
}

template <typename T> void BinaryOperator::Eval_BinAnd_T(void *out)
{
	SubExpr1->Eval(out);
	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out &= rhs;
}

template <typename T> void BinaryOperator::Eval_BinOr_T(void *out)
{
	SubExpr1->Eval(out);
	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out |= rhs;
}

template <typename T> void BinaryOperator::Eval_BinXor_T(void *out)
{
	SubExpr1->Eval(out);
	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out ^= rhs;
}

template <typename T> void BinaryOperator::Eval_BinShiftLeft_T(void *out)
{
	SubExpr1->Eval(out);
	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out <<= rhs;
}

template <typename T> void BinaryOperator::Eval_BinShiftRight_T(void *out)
{
	SubExpr1->Eval(out);
	T rhs;
	SubExpr2->Eval(&rhs);
	*(T*)out >>= rhs;
}

template <typename T>
void BinaryOperator::Eval_AssignNumAdd_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue += rval;
}

template <typename T>
void BinaryOperator::Eval_AssignNumSub_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue -= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignNumMul_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue *= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignIntegerDiv_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	if (rval == T())
		throw Error("Integer division by zero");
	*(T*)lvalue /= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignRealDiv_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue /= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignIntegerMod_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	if (rval == T())
		throw Error("Integer modulo division by zero");
	*(T*)lvalue %= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignBinAnd_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue &= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignBinOr_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue |= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignBinXor_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue ^= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignBinShiftLeft_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue <<= rval;
}

template <typename T>
void BinaryOperator::Eval_AssignBinShiftRight_T(void *lvalue)
{
	T rval;
	SubExpr2->Eval(&rval);
	*(T*)lvalue >>= rval;
}

void BinaryOperator::Eval_NumAdd(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_NumAdd_T<uint1>(out); break;
	case Type::TYPE_UINT16: Eval_NumAdd_T<uint2>(out); break;
	case Type::TYPE_UINT32: Eval_NumAdd_T<uint4>(out); break;
	case Type::TYPE_UINT64: Eval_NumAdd_T<uint8>(out); break;
	case Type::TYPE_INT8:   Eval_NumAdd_T<int1>(out); break;
	case Type::TYPE_INT16:  Eval_NumAdd_T<int2>(out); break;
	case Type::TYPE_INT32:  Eval_NumAdd_T<int4>(out); break;
	case Type::TYPE_INT64:  Eval_NumAdd_T<int8>(out); break;
	case Type::TYPE_FLOAT:  Eval_NumAdd_T<float>(out); break;
	case Type::TYPE_DOUBLE: Eval_NumAdd_T<double>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_NumSub(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_NumSub_T<uint1>(out); break;
	case Type::TYPE_UINT16: Eval_NumSub_T<uint2>(out); break;
	case Type::TYPE_UINT32: Eval_NumSub_T<uint4>(out); break;
	case Type::TYPE_UINT64: Eval_NumSub_T<uint8>(out); break;
	case Type::TYPE_INT8:   Eval_NumSub_T<int1>(out); break;
	case Type::TYPE_INT16:  Eval_NumSub_T<int2>(out); break;
	case Type::TYPE_INT32:  Eval_NumSub_T<int4>(out); break;
	case Type::TYPE_INT64:  Eval_NumSub_T<int8>(out); break;
	case Type::TYPE_FLOAT:  Eval_NumSub_T<float>(out); break;
	case Type::TYPE_DOUBLE: Eval_NumSub_T<double>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_NumMul(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_NumMul_T<uint1>(out); break;
	case Type::TYPE_UINT16: Eval_NumMul_T<uint2>(out); break;
	case Type::TYPE_UINT32: Eval_NumMul_T<uint4>(out); break;
	case Type::TYPE_UINT64: Eval_NumMul_T<uint8>(out); break;
	case Type::TYPE_INT8:   Eval_NumMul_T<int1>(out); break;
	case Type::TYPE_INT16:  Eval_NumMul_T<int2>(out); break;
	case Type::TYPE_INT32:  Eval_NumMul_T<int4>(out); break;
	case Type::TYPE_INT64:  Eval_NumMul_T<int8>(out); break;
	case Type::TYPE_FLOAT:  Eval_NumMul_T<float>(out); break;
	case Type::TYPE_DOUBLE: Eval_NumMul_T<double>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_NumDiv(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_IntegerDiv_T<uint1>(out); break;
	case Type::TYPE_UINT16: Eval_IntegerDiv_T<uint2>(out); break;
	case Type::TYPE_UINT32: Eval_IntegerDiv_T<uint4>(out); break;
	case Type::TYPE_UINT64: Eval_IntegerDiv_T<uint8>(out); break;
	case Type::TYPE_INT8:   Eval_IntegerDiv_T<int1>(out); break;
	case Type::TYPE_INT16:  Eval_IntegerDiv_T<int2>(out); break;
	case Type::TYPE_INT32:  Eval_IntegerDiv_T<int4>(out); break;
	case Type::TYPE_INT64:  Eval_IntegerDiv_T<int8>(out); break;
	case Type::TYPE_FLOAT:  Eval_RealDiv_T<float>(out); break;
	case Type::TYPE_DOUBLE: Eval_RealDiv_T<double>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_IntegerMod(void *out)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_IntegerMod_T<uint1>(out); break;
	case Type::TYPE_UINT16: Eval_IntegerMod_T<uint2>(out); break;
	case Type::TYPE_UINT32: Eval_IntegerMod_T<uint4>(out); break;
	case Type::TYPE_UINT64: Eval_IntegerMod_T<uint8>(out); break;
	case Type::TYPE_INT8:   Eval_IntegerMod_T<int1>(out); break;
	case Type::TYPE_INT16:  Eval_IntegerMod_T<int2>(out); break;
	case Type::TYPE_INT32:  Eval_IntegerMod_T<int4>(out); break;
	case Type::TYPE_INT64:  Eval_IntegerMod_T<int8>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_BinAnd(void *out)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_BinAnd_T<uint1>(out); break;
	case 2: Eval_BinAnd_T<uint2>(out); break;
	case 4: Eval_BinAnd_T<uint4>(out); break;
	case 8: Eval_BinAnd_T<uint8>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_BinOr(void *out)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_BinOr_T<uint1>(out); break;
	case 2: Eval_BinOr_T<uint2>(out); break;
	case 4: Eval_BinOr_T<uint4>(out); break;
	case 8: Eval_BinOr_T<uint8>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_BinXor(void *out)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_BinXor_T<uint1>(out); break;
	case 2: Eval_BinXor_T<uint2>(out); break;
	case 4: Eval_BinXor_T<uint4>(out); break;
	case 8: Eval_BinXor_T<uint8>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_BinShiftLeft(void *out)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_BinShiftLeft_T<uint1>(out); break;
	case 2: Eval_BinShiftLeft_T<uint2>(out); break;
	case 4: Eval_BinShiftLeft_T<uint4>(out); break;
	case 8: Eval_BinShiftLeft_T<uint8>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_BinShiftRight(void *out)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_BinShiftRight_T<uint1>(out); break;
	case 2: Eval_BinShiftRight_T<uint2>(out); break;
	case 4: Eval_BinShiftRight_T<uint4>(out); break;
	case 8: Eval_BinShiftRight_T<uint8>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignNumAdd(void *lvalue)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_AssignNumAdd_T<uint1>(lvalue); break;
	case Type::TYPE_UINT16: Eval_AssignNumAdd_T<uint2>(lvalue); break;
	case Type::TYPE_UINT32: Eval_AssignNumAdd_T<uint4>(lvalue); break;
	case Type::TYPE_UINT64: Eval_AssignNumAdd_T<uint8>(lvalue); break;
	case Type::TYPE_INT8:   Eval_AssignNumAdd_T<int1>(lvalue); break;
	case Type::TYPE_INT16:  Eval_AssignNumAdd_T<int2>(lvalue); break;
	case Type::TYPE_INT32:  Eval_AssignNumAdd_T<int4>(lvalue); break;
	case Type::TYPE_INT64:  Eval_AssignNumAdd_T<int8>(lvalue); break;
	case Type::TYPE_FLOAT:  Eval_AssignNumAdd_T<float>(lvalue); break;
	case Type::TYPE_DOUBLE: Eval_AssignNumAdd_T<double>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignNumSub(void *lvalue)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_AssignNumSub_T<uint1>(lvalue); break;
	case Type::TYPE_UINT16: Eval_AssignNumSub_T<uint2>(lvalue); break;
	case Type::TYPE_UINT32: Eval_AssignNumSub_T<uint4>(lvalue); break;
	case Type::TYPE_UINT64: Eval_AssignNumSub_T<uint8>(lvalue); break;
	case Type::TYPE_INT8:   Eval_AssignNumSub_T<int1>(lvalue); break;
	case Type::TYPE_INT16:  Eval_AssignNumSub_T<int2>(lvalue); break;
	case Type::TYPE_INT32:  Eval_AssignNumSub_T<int4>(lvalue); break;
	case Type::TYPE_INT64:  Eval_AssignNumSub_T<int8>(lvalue); break;
	case Type::TYPE_FLOAT:  Eval_AssignNumSub_T<float>(lvalue); break;
	case Type::TYPE_DOUBLE: Eval_AssignNumSub_T<double>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignNumMul(void *lvalue)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_AssignNumMul_T<uint1>(lvalue); break;
	case Type::TYPE_UINT16: Eval_AssignNumMul_T<uint2>(lvalue); break;
	case Type::TYPE_UINT32: Eval_AssignNumMul_T<uint4>(lvalue); break;
	case Type::TYPE_UINT64: Eval_AssignNumMul_T<uint8>(lvalue); break;
	case Type::TYPE_INT8:   Eval_AssignNumMul_T<int1>(lvalue); break;
	case Type::TYPE_INT16:  Eval_AssignNumMul_T<int2>(lvalue); break;
	case Type::TYPE_INT32:  Eval_AssignNumMul_T<int4>(lvalue); break;
	case Type::TYPE_INT64:  Eval_AssignNumMul_T<int8>(lvalue); break;
	case Type::TYPE_FLOAT:  Eval_AssignNumMul_T<float>(lvalue); break;
	case Type::TYPE_DOUBLE: Eval_AssignNumMul_T<double>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignNumDiv(void *lvalue)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_AssignIntegerDiv_T<uint1>(lvalue); break;
	case Type::TYPE_UINT16: Eval_AssignIntegerDiv_T<uint2>(lvalue); break;
	case Type::TYPE_UINT32: Eval_AssignIntegerDiv_T<uint4>(lvalue); break;
	case Type::TYPE_UINT64: Eval_AssignIntegerDiv_T<uint8>(lvalue); break;
	case Type::TYPE_INT8:   Eval_AssignIntegerDiv_T<int1>(lvalue); break;
	case Type::TYPE_INT16:  Eval_AssignIntegerDiv_T<int2>(lvalue); break;
	case Type::TYPE_INT32:  Eval_AssignIntegerDiv_T<int4>(lvalue); break;
	case Type::TYPE_INT64:  Eval_AssignIntegerDiv_T<int8>(lvalue); break;
	case Type::TYPE_FLOAT:  Eval_AssignRealDiv_T<float>(lvalue); break;
	case Type::TYPE_DOUBLE: Eval_AssignRealDiv_T<double>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignNumMod(void *lvalue)
{
	switch (ExprType.GetType())
	{
	case Type::TYPE_UINT8:  Eval_AssignIntegerMod_T<uint1>(lvalue); break;
	case Type::TYPE_UINT16: Eval_AssignIntegerMod_T<uint2>(lvalue); break;
	case Type::TYPE_UINT32: Eval_AssignIntegerMod_T<uint4>(lvalue); break;
	case Type::TYPE_UINT64: Eval_AssignIntegerMod_T<uint8>(lvalue); break;
	case Type::TYPE_INT8:   Eval_AssignIntegerMod_T<int1>(lvalue); break;
	case Type::TYPE_INT16:  Eval_AssignIntegerMod_T<int2>(lvalue); break;
	case Type::TYPE_INT32:  Eval_AssignIntegerMod_T<int4>(lvalue); break;
	case Type::TYPE_INT64:  Eval_AssignIntegerMod_T<int8>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignBinAnd(void *lvalue)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_AssignBinAnd_T<uint1>(lvalue); break;
	case 2: Eval_AssignBinAnd_T<uint2>(lvalue); break;
	case 4: Eval_AssignBinAnd_T<uint4>(lvalue); break;
	case 8: Eval_AssignBinAnd_T<uint8>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignBinOr(void *lvalue)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_AssignBinOr_T<uint1>(lvalue); break;
	case 2: Eval_AssignBinOr_T<uint2>(lvalue); break;
	case 4: Eval_AssignBinOr_T<uint4>(lvalue); break;
	case 8: Eval_AssignBinOr_T<uint8>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignBinXor(void *lvalue)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_AssignBinXor_T<uint1>(lvalue); break;
	case 2: Eval_AssignBinXor_T<uint2>(lvalue); break;
	case 4: Eval_AssignBinXor_T<uint4>(lvalue); break;
	case 8: Eval_AssignBinXor_T<uint8>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignBinShiftLeft(void *lvalue)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_AssignBinShiftLeft_T<uint1>(lvalue); break;
	case 2: Eval_AssignBinShiftLeft_T<uint2>(lvalue); break;
	case 4: Eval_AssignBinShiftLeft_T<uint4>(lvalue); break;
	case 8: Eval_AssignBinShiftLeft_T<uint8>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval_AssignBinShiftRight(void *lvalue)
{
	switch (ExprType.GetSize())
	{
	case 1: Eval_AssignBinShiftRight_T<uint1>(lvalue); break;
	case 2: Eval_AssignBinShiftRight_T<uint2>(lvalue); break;
	case 4: Eval_AssignBinShiftRight_T<uint4>(lvalue); break;
	case 8: Eval_AssignBinShiftRight_T<uint8>(lvalue); break;
	default: assert(0);
	}
}

void BinaryOperator::Eval(void *out)
{
	switch (OpType)
	{
	case OP_NUM_ADDITION:       Eval_NumAdd(out); break;
	case OP_NUM_SUBTRACTION:    Eval_NumSub(out); break;
	case OP_NUM_MULTIPLICATION: Eval_NumMul(out); break;
	case OP_NUM_DIVISION:       Eval_NumDiv(out); break;
	case OP_NUM_MODULO:         Eval_IntegerMod(out); break;
	case OP_BIN_AND:            Eval_BinAnd(out); break;
	case OP_BIN_OR:             Eval_BinOr(out); break;
	case OP_BIN_XOR:            Eval_BinXor(out); break;
	case OP_BIN_SHIFT_LEFT:     Eval_BinShiftLeft(out); break;
	case OP_BIN_SHIFT_RIGHT:    Eval_BinShiftRight(out); break;

	case OP_CMP_EQUAL:
	case OP_CMP_NOT_EQUAL:
		EvalEqual((bool*)out);
		break;

	case OP_CMP_LESS:
	case OP_CMP_GREATER:
	case OP_CMP_LESS_EQUAL:
	case OP_CMP_GREATER_EQUAL:
		EvalCompare((bool*)out);
		break;

	case OP_LOG_AND:
	case OP_LOG_OR:
		{
			bool lhs, *outVal = (bool*)out;
			SubExpr1->Eval(&lhs);
			switch (OpType)
			{
			case OP_LOG_AND:
				if (lhs == false)
					*outVal = false;
				else
					SubExpr2->Eval(outVal);
				break;
			case OP_LOG_OR:
				if (lhs == true)
					*outVal = true;
				else
					SubExpr2->Eval(outVal);
				break;
			default:
				assert(0);
			}
		}
		break;

	case OP_STR_CONCAT:
		EvalStrConcat((string*)out);
		break;
	case OP_RETURN_LEFT:
		EvalReturnLeft(out);
		break;

	// Assignment operators
	case OP_ASSIGN:
	case OP_ASSIGN_NUM_ADD:
	case OP_ASSIGN_NUM_SUB:
	case OP_ASSIGN_NUM_MUL:
	case OP_ASSIGN_NUM_DIV:
	case OP_ASSIGN_NUM_MOD:
	case OP_ASSIGN_STR_CONCAT:
	case OP_ASSIGN_BIN_AND:
	case OP_ASSIGN_BIN_OR:
	case OP_ASSIGN_BIN_XOR:
	case OP_ASSIGN_BIN_SHIFT_LEFT:
	case OP_ASSIGN_BIN_SHIFT_RIGHT:
		EvalAssignment(out);
		break;

	default:
		assert(0);
	}
}

void BinaryOperator::EvalStrConcat( string *out )
{
	if (SubExpr1->ExprType == Type(Type::TYPE_STRING))
		SubExpr1->Eval(out);
	else
	{
		assert(SubExpr1->ExprType == Type(Type::TYPE_CHAR));
		out->resize(1);
		SubExpr1->Eval(&(*out)[0]);
	}

	if (SubExpr2->ExprType == Type(Type::TYPE_STRING))
	{
		string rhs;
		SubExpr2->Eval(&rhs);
		*out += rhs;
	}
	else
	{
		assert(SubExpr2->ExprType == Type(Type::TYPE_CHAR));
		size_t len = out->length();
		out->resize(len+1);
		SubExpr2->Eval(&(*out)[len]);
	}
}

bool BinaryOperator::TypeIsComparable( const Type &type )
{
	return type.GetType() == Type::TYPE_INT8
		|| type.GetType() == Type::TYPE_INT16
		|| type.GetType() == Type::TYPE_INT32
		|| type.GetType() == Type::TYPE_INT64
		|| type.GetType() == Type::TYPE_UINT8
		|| type.GetType() == Type::TYPE_UINT16
		|| type.GetType() == Type::TYPE_UINT32
		|| type.GetType() == Type::TYPE_UINT64
		|| type.GetType() == Type::TYPE_FLOAT
		|| type.GetType() == Type::TYPE_DOUBLE
		|| type.GetType() == Type::TYPE_CHAR
		|| type.GetType() == Type::TYPE_STRING;
}

void BinaryOperator::EvalEqual( bool *out )
{
	Value val1(SubExprType), val2(SubExprType);
	SubExpr1->Eval(val1.GetAddr());
	SubExpr2->Eval(val2.GetAddr());
	
	*out = SubExprType.ValuesEqual(val1.GetAddr(), val2.GetAddr());
	
	if (OpType == OP_CMP_NOT_EQUAL)
		*out = ! *out;
}

template <typename T> void BinaryOperator::EvalCompare_T(bool *out)
{
	T lhs, rhs;
	SubExpr1->Eval(&lhs);
	SubExpr2->Eval(&rhs);

	switch (OpType)
	{
	case OP_CMP_LESS:          *out = lhs <  rhs; break;
	case OP_CMP_GREATER:       *out = lhs >  rhs; break;
	case OP_CMP_LESS_EQUAL:    *out = lhs <= rhs; break;
	case OP_CMP_GREATER_EQUAL: *out = lhs >= rhs; break;
	default: assert(0);
	}
}

void BinaryOperator::EvalCompare( bool *out )
{
	switch (SubExprType.GetType())
	{
	case Type::TYPE_UINT8:  EvalCompare_T<uint1>(out); break;
	case Type::TYPE_UINT16: EvalCompare_T<uint2>(out); break;
	case Type::TYPE_UINT32: EvalCompare_T<uint4>(out); break;
	case Type::TYPE_UINT64: EvalCompare_T<uint8>(out); break;
	case Type::TYPE_INT8:   EvalCompare_T<int1>(out); break;
	case Type::TYPE_INT16:  EvalCompare_T<int2>(out); break;
	case Type::TYPE_INT32:  EvalCompare_T<int4>(out); break;
	case Type::TYPE_INT64:  EvalCompare_T<int8>(out); break;
	case Type::TYPE_FLOAT:  EvalCompare_T<float>(out); break;
	case Type::TYPE_DOUBLE: EvalCompare_T<double>(out); break;
	case Type::TYPE_CHAR:   EvalCompare_T<uint1>(out); break;
	case Type::TYPE_STRING: EvalCompare_T<string>(out); break;
	default: assert(0);
	}
}

void BinaryOperator::EvalReturnLeft( void *out )
{
	SubExpr1->Eval(out);
	SubExpr2->Eval(NULL);
}

bool BinaryOperator::SymbolIsAssignmentOperator( const string &s )
{
	return s == OP_TYPE_SYMBOLS[OP_ASSIGN]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_NUM_ADD]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_NUM_SUB]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_NUM_MUL]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_NUM_DIV]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_NUM_MOD]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_BIN_AND]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_BIN_OR]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_BIN_XOR]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_BIN_SHIFT_LEFT]
		|| s == OP_TYPE_SYMBOLS[OP_ASSIGN_BIN_SHIFT_RIGHT];
}

BinaryOperator::OP_TYPE BinaryOperator::SymbolToOpType(const string &s)
{
	for (uint i = 0; i < OP_TYPE_COUNT; i++)
		if (OP_TYPE_SYMBOLS[i] && s == OP_TYPE_SYMBOLS[i])
			return (OP_TYPE)i;
	return OP_TYPE_COUNT;
}

void BinaryOperator::PreprocessAssignment(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	if (!SubExpr1->IsLvalue())
		throw Error("Expression on the left of assignment operator must be an L-value");

	switch (OpType)
	{
	// Pure assignment - any type
	case OP_ASSIGN:
		PreprocessExpressionNode(SubExpr1, ctx, typeSuggestion);
		PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(SubExpr1->ExprType, 0));
		ExprType = SubExpr1->ExprType;
		if (!ExprType.CheckClass(Type::CLASS_PHYSICAL))
			throw Error("Cannot assign to type " + ExprType.GetName_r());
		EnsureExpressionType(SubExpr2, ExprType, ctx);
		break;

	// Operators for integers:
	case OP_ASSIGN_NUM_MOD:
	case OP_ASSIGN_BIN_AND:
	case OP_ASSIGN_BIN_OR:
	case OP_ASSIGN_BIN_XOR:
	case OP_ASSIGN_BIN_SHIFT_LEFT:
	case OP_ASSIGN_BIN_SHIFT_RIGHT:
		{
			TypeSuggestion newSuggestion = typeSuggestion;
			if (newSuggestion.IsEmpty() || newSuggestion.T.GetType() == Type::TYPE_VOID)
				newSuggestion = TypeSuggestion(Type(), Type::CLASS_INTEGER | Type::CLASS_UNSIGNED);
			PreprocessExpressionNode(SubExpr1, ctx, newSuggestion);
			if (!SubExpr1->ExprType.CheckClass(Type::CLASS_INTEGER))
				throw Error("Left expression must be of integer type");
			ExprType = SubExpr1->ExprType;
			PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(ExprType, 0));
			EnsureExpressionType(SubExpr2, ExprType, ctx);
		}
		break;

	// Addition or string concatenation:
	case OP_ASSIGN_NUM_ADD:
		{
			TypeSuggestion newSuggestion = typeSuggestion;
			if (newSuggestion.IsEmpty() || newSuggestion.T.GetType() == Type::TYPE_VOID)
				newSuggestion = TypeSuggestion(Type(), Type::CLASS_NUMERIC);
			PreprocessExpressionNode(SubExpr1, ctx, newSuggestion);
			ExprType = SubExpr1->ExprType;
			if (SubExpr1->ExprType == Type(Type::TYPE_STRING))
			{
				OpType = OP_ASSIGN_STR_CONCAT;
				PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(ExprType, 0));
				EnsureExpressionType(SubExpr2, ExprType, ctx);
				break;
			}
			else
			{
				if (!SubExpr1->ExprType.CheckClass(Type::CLASS_NUMERIC))
					throw Error("Left expression must be of numeric or string type");
				PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(ExprType, 0));
				EnsureExpressionType(SubExpr2, ExprType, ctx);
			}
		}
		break;

	// Operators for numeric types:
	case OP_ASSIGN_NUM_SUB:
	case OP_ASSIGN_NUM_MUL:
	case OP_ASSIGN_NUM_DIV:
		{
			TypeSuggestion newSuggestion = typeSuggestion;
			if (newSuggestion.IsEmpty() || newSuggestion.T.GetType() == Type::TYPE_VOID)
				newSuggestion = TypeSuggestion(Type(), Type::CLASS_NUMERIC);
			PreprocessExpressionNode(SubExpr1, ctx, newSuggestion);
			if (!SubExpr1->ExprType.CheckClass(Type::CLASS_NUMERIC))
				throw Error("Left expression must be of numeric type");
			ExprType = SubExpr1->ExprType;
			PreprocessExpressionNode(SubExpr2, ctx, TypeSuggestion(ExprType, 0));
			EnsureExpressionType(SubExpr2, ExprType, ctx);
		}
		break;

	default:
		assert(0);
	}
}

bool BinaryOperator::IsLvalue()
{
	if (OpType == OP_RETURN_LEFT)
		return SubExpr1->IsLvalue();
	return false;
}

void * BinaryOperator::AccessLvalue()
{
	if (OpType == OP_RETURN_LEFT)
		return SubExpr1->AccessLvalue();
	return NULL;
}

void BinaryOperator::EvalAssignment( void *out )
{
	void *lvalue = SubExpr1->AccessLvalue();
	assert(lvalue);

	switch (OpType)
	{
	// Pure assignment
	case OP_ASSIGN:
		SubExpr2->Eval(lvalue);
		break;

	// Numeric operations
	case OP_ASSIGN_NUM_ADD: Eval_AssignNumAdd(lvalue); break;
	case OP_ASSIGN_NUM_SUB: Eval_AssignNumSub(lvalue); break;
	case OP_ASSIGN_NUM_MUL: Eval_AssignNumMul(lvalue); break;
	case OP_ASSIGN_NUM_DIV: Eval_AssignNumDiv(lvalue); break;
	case OP_ASSIGN_NUM_MOD: Eval_AssignNumMod(lvalue); break;

	// String concatenation
	case OP_ASSIGN_STR_CONCAT:
		{
			assert(ExprType == Type(Type::TYPE_STRING));
			string rval;
			SubExpr2->Eval(&rval);
			*(string*)lvalue += rval;
		}
		break;

	// Bit operators
	case OP_ASSIGN_BIN_AND:         Eval_AssignBinAnd(lvalue); break;
	case OP_ASSIGN_BIN_OR:          Eval_AssignBinOr(lvalue); break;
	case OP_ASSIGN_BIN_XOR:         Eval_AssignBinXor(lvalue); break;
	case OP_ASSIGN_BIN_SHIFT_LEFT:  Eval_AssignBinShiftLeft(lvalue); break;
	case OP_ASSIGN_BIN_SHIFT_RIGHT: Eval_AssignBinShiftRight(lvalue); break;
	}

	ExprType.CopyValue(out, lvalue);
}

void BinaryOperator::CheckIntDivDenom( int denom )
{
	if (denom == 0)
		throw Error("Integer division by zero");
}

void IdentifierExpression::PrintDebug( uint level )
{
	PrintDebugIndent(level);
	std::cout << "Variable R-value: " << Name << std::endl;
}

ExpressionPtr IdentifierExpression::Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	if (!g_Context.VarMan.Find(Name, &ExprType, NULL))
		throw Error("Variable " + Name + " not found");
	return ExpressionPtr();
}

void IdentifierExpression::Eval( void *out )
{
	ExprType.CopyValue(out, g_Context.VarMan.AccessValue(Name));
}

bool IdentifierExpression::IsLvalue()
{
	// TODO: false when variable is constant.
	return true;
}

void * IdentifierExpression::AccessLvalue()
{
	return g_Context.VarMan.AccessValue(Name);
}

ExpressionPtr FunctionCall::Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	// Theoretically I could include typeSuggestion into finding correct overloaded function version.

	IdentifierExpression *funcIdentifierExpr = dynamic_cast<IdentifierExpression*>(FuncExpr.get());
	if (funcIdentifierExpr == NULL)
		throw Error("Expected identifier containing function name");
	string &funcName = funcIdentifierExpr->Name;

	std::vector<TypeSuggestion> paramTypeSuggestions;
	uint actualParamCount = Params.size();
	if (!g_Context.FuncMan.SuggestTypes(paramTypeSuggestions, funcName, actualParamCount))
		throw Error("Function " + funcName + " not found");
	std::vector<Type> paramTypes(actualParamCount);
	for (uint i = 0; i < actualParamCount; i++)
	{
		PreprocessExpressionNode(Params[i], ctx, paramTypeSuggestions[i]);
		paramTypes[i] = Params[i]->ExprType;
	}
	// (Here paramTypes contain actual param types, size = actualParamCount.)

	FunctionManager::FIND_RESULT findResult = g_Context.FuncMan.Find(funcName, m_RegisteredFunc, actualParamCount ? &paramTypes[0] : NULL, actualParamCount, ctx.Rules);
	if (findResult != FunctionManager::FIND_RESULT_OK)
	{
		switch (findResult)
		{
		case FunctionManager::FIND_RESULT_NOT_FOUND:
			throw Error("Function " + funcName + " not found");
		case FunctionManager::FIND_RESULT_PARAMS_NOT_MATCH:
		case FunctionManager::FIND_RESULT_AMBIGUOUS:
			{
				string typesStr;
				if (paramTypes.empty())
					typesStr = "0 parameters";
				else
				{
					string typesListStr;
					TypeVectorToStr(typesListStr, paramTypes);
					typesStr = UintToStrR(paramTypes.size()) + " parameters: " + typesListStr;
				}

				string errMsg;
				if (findResult == FunctionManager::FIND_RESULT_PARAMS_NOT_MATCH)
					errMsg = "No matching function " + funcName + " found for " + typesStr + ".";
				else //if (findResult == FunctionManager::FIND_RESULT_AMBIGUOUS)
					errMsg = "Ambiguous call to overloaded function " + funcName + " for " + typesStr + ".";

				string proposedFuncSigs;
				g_Context.FuncMan.GetSignatures(proposedFuncSigs, funcName, &string("  "));
				throw Error(errMsg + " Available functions:\n"+proposedFuncSigs);
			}
		default:
			assert(0);
		}
	}

	assert(m_RegisteredFunc);
	ExprType = m_RegisteredFunc->ResultType;
	// (Here paramTypes contain formal param types, size = formalParamCount.)

	// Adjust actual parameter types to formal ones.
	for (size_t i = 0; i < actualParamCount; i++)
		EnsureExpressionType(Params[i], m_RegisteredFunc->ParamTypes[i], ctx);

	/*size_t paramCount = Params.size();
	ParamTypes.resize(paramCount);
	for (size_t i = 0; i < paramCount; i++)
	{
		Params[i]->Preprocess(ctx);
		ParamTypes[i] = Params[i]->ExprType;
	}

	FunctionId = g_Context.FuncMan.Find(funcName, ExprType, paramCount ? &ParamTypes[0] : NULL, paramCount, Variadic);
	if (FunctionId == ID_NULL)
		throw Error("Function " + funcName + " not found");
	else if (FunctionId == ID_INVALID_PARAMS)
		throw Error("Invalid parameters for function " + funcName);*/

	return ExpressionPtr();
}

void FunctionCall::Eval( void *out )
{
	assert(m_RegisteredFunc);

	uint actualParamCount = Params.size();

	if (actualParamCount == 0)
		m_RegisteredFunc->Exec(out, NULL, 0);
	else
	{
		// Allocate and evaluate actual params
		// TODO: maybe there is a way to optimize it so evaluated parameter values can be saved directly to parameter variables, not to this vector and then copied to parameter variables in FunctionParamDef::Call* ?
		std::vector<ValuePtr> paramValues(actualParamCount);
		std::vector<void*> paramAddrs(actualParamCount);
		for (uint i = 0; i < actualParamCount; i++)
		{
			paramValues[i].reset(ValuePtr(new Value(Params[i]->ExprType)));
			paramAddrs[i] = paramValues[i]->GetAddr();
			Params[i]->Eval(paramAddrs[i]);
		}

		// Execute function
		m_RegisteredFunc->Exec(out, &paramAddrs[0], actualParamCount);
	}
}

ExpressionPtr ConditionalOperator::Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion)
{
	PreprocessExpressionNode(CondExpr, ctx, TypeSuggestion(Type(Type::TYPE_BOOL), 0));
	EnsureExpressionType(CondExpr, Type(Type::TYPE_BOOL), ctx);

	PreprocessExpressionNode(TrueExpr,  ctx, typeSuggestion);
	PreprocessExpressionNode(FalseExpr, ctx, typeSuggestion);
	ExprType.Promote(TrueExpr->ExprType, FalseExpr->ExprType);
	EnsureExpressionType(TrueExpr,  ExprType, ctx);
	EnsureExpressionType(FalseExpr, ExprType, ctx);

	return ExpressionPtr();
}

void ConditionalOperator::PrintDebug( uint level )
{
	PrintDebugIndent(level);
	std::cout << "Conditional operator" << std::endl;

	++level;
	CondExpr->PrintDebug(level);
	TrueExpr->PrintDebug(level);
	FalseExpr->PrintDebug(level);
}

void ConditionalOperator::Eval( void *out )
{
	bool bval;
	CondExpr->Eval(&bval);
	if (bval)
		TrueExpr->Eval(out);
	else
		FalseExpr->Eval(out);
}

bool ConditionalOperator::IsLvalue()
{
	return TrueExpr->IsLvalue() && FalseExpr->IsLvalue();
}

void * ConditionalOperator::AccessLvalue()
{
	bool bval;
	CondExpr->Eval(&bval);
	if (bval)
		return TrueExpr->AccessLvalue();
	else
		return FalseExpr->AccessLvalue();
}

void ExpressionStatement::PrintDebug( uint level )
{
	PrintDebugIndent(level);
	std::cout << "Expression statement" << std::endl;
	Expr->PrintDebug(level+1);
}

StatementPtr ExpressionStatement::Preprocess(PreprocessContext &ctx)
{
	Type voidType = Type(Type::TYPE_VOID);
	PreprocessExpressionNode(Expr, ctx, TypeSuggestion(voidType, 0));
	EnsureExpressionType(Expr, voidType, ctx);
	return StatementPtr();
}

void ExpressionStatement::Exec()
{
	Expr->Eval(NULL);
}

ExpressionStatement::~ExpressionStatement()
{

}

void BlockStatement::PrintDebug( uint level )
{
	PrintDebugIndent(level);
	std::cout << "Statement block" << std::endl;
	
	++level;
	for (uint i = 0; i < SubStatements.size(); i++)
		SubStatements[i]->PrintDebug(level);
}

StatementPtr BlockStatement::Preprocess( PreprocessContext &ctx )
{
	ContextBlock ctxBlock;
	PreprocessSubStatements(ctx);
	return StatementPtr();
}

void BlockStatement::Exec()
{
	ContextBlock ctxBlock;
	ExecSubStatements();
}

void StatementCollection::PreprocessSubStatements(PreprocessContext &ctx)
{
	for (uint i = 0; i < SubStatements.size(); i++)
		PreprocessStatementNode(SubStatements[i], ctx);
}

void StatementCollection::ExecSubStatements()
{
	for (uint i = 0; i < SubStatements.size(); i++)
		SubStatements[i]->Exec();
}


StatementPtr IfStatement::Preprocess(PreprocessContext &ctx)
{
	Type boolType = Type(Type::TYPE_BOOL);
	PreprocessExpressionNode(CondExpr, ctx, TypeSuggestion(boolType, 0));
	EnsureExpressionType(CondExpr, boolType, ctx);

	if (!TrueStatement.is_null())
	{
		ContextBlock contextBlock;
		PreprocessStatementNode(TrueStatement, ctx);
	}
	if (!FalseStatement.is_null())
	{
		ContextBlock contextBlock;
		PreprocessStatementNode(FalseStatement, ctx);
	}

	return StatementPtr();
}

void IfStatement::Exec()
{
	bool val;
	CondExpr->Eval(&val);
	
	if (val)
	{
		if (!TrueStatement.is_null())
		{
			ContextBlock contextBlock;
			TrueStatement->Exec();
		}
	}
	else
	{
		if (!FalseStatement.is_null())
		{
			ContextBlock contextBlock;
			FalseStatement->Exec();
		}
	}
}

WhileStatement::~WhileStatement()
{
}

StatementPtr WhileStatement::Preprocess(PreprocessContext &ctx)
{
	Type boolType = Type(Type::TYPE_BOOL);
	PreprocessExpressionNode(CondExpr, ctx, TypeSuggestion(boolType, 0));
	EnsureExpressionType(CondExpr, boolType, ctx);

	if (!SubStatement.is_null())
	{
		ContextBlock contextBlock;
		PreprocessContext::FlagsObj flagObj(ctx, PreprocessContext::FLAG_ENABLE_BREAK | PreprocessContext::FLAG_ENABLE_CONTINUE);
		PreprocessStatementNode(SubStatement, ctx);
	}

	return StatementPtr();
}

void WhileStatement::Exec()
{
	bool bval;
	for (;;)
	{
		CondExpr->Eval(&bval);
		if (!bval)
			break;

		try
		{
			ContextBlock contextBlock;
			SubStatement->Exec();
		}
		catch (const BreakException&)
		{
			return;
		}
		catch (const ContinueException&)
		{
			// Go further.
		}
	}
}

DoWhileStatement::~DoWhileStatement()
{
}

StatementPtr DoWhileStatement::Preprocess(PreprocessContext &ctx)
{
	if (!SubStatement.is_null())
	{
		ContextBlock contextBlock;
		PreprocessContext::FlagsObj flagObj(ctx, PreprocessContext::FLAG_ENABLE_BREAK | PreprocessContext::FLAG_ENABLE_CONTINUE);
		PreprocessStatementNode(SubStatement, ctx);
	}

	Type boolType = Type(Type::TYPE_BOOL);
	PreprocessExpressionNode(CondExpr, ctx, TypeSuggestion(boolType, 0));
	EnsureExpressionType(CondExpr, boolType, ctx);

	return StatementPtr();
}

void DoWhileStatement::Exec()
{
	bool bval;
	for (;;)
	{
		try
		{
			ContextBlock contextBlock;
			SubStatement->Exec();
		}
		catch (const BreakException&)
		{
			return;
		}
		catch (const ContinueException&)
		{
			// Go further.
		}

		CondExpr->Eval(&bval);
		if (!bval)
			break;
	}
}

void VariableDef::Exec()
{
	void *addr = RegisterInContext();
	if (!InitialValue.is_null())
		InitialValue->Eval(addr);
	else
		ExprType.InitValue(addr);
}

void VariableDef::PreprocessInitialValue(PreprocessContext &ctx)
{
	if (!InitialValue.is_null())
		PreprocessExpressionNode(InitialValue, ctx, TypeSuggestion(ExprType, 0));
}

void VariableDef::InferType()
{
	if (ExprType.GetType() == Type::TYPE_AUTO)
	{
		if (!InitialValue.is_null())
			ExprType = InitialValue->ExprType;
		else
			throw Error("Cannot determine type for variable " + Name);
	}
}

void VariableDef::ValidateType()
{
	if (!ExprType.CheckClass(Type::CLASS_PHYSICAL))
		throw Error("Invalid type for variable " + Name);
}

void VariableDef::ValidateNameInContext()
{
	if (g_Context.VarMan.FindLocal(Name, NULL, NULL))
		throw Error("Variable " + Name + " already defined in this scope");
}

void * VariableDef::RegisterInContext()
{
	return g_Context.VarMan.New(Name, ExprType, false);
}

void VariableDef::AdjustInitialValueType(PreprocessContext &ctx)
{
	if (!InitialValue.is_null())
		EnsureExpressionType(InitialValue, ExprType, ctx);
}

StatementPtr ForStatement::Preprocess(PreprocessContext &ctx)
{
	ContextBlock contextBlock;

	if (!InitStatement.is_null())
		PreprocessStatementNode(InitStatement, ctx);

	if (!CondExpr.is_null())
	{
		Type boolType = Type(Type::TYPE_BOOL);
		PreprocessExpressionNode(CondExpr, ctx, TypeSuggestion(boolType, 0));
		EnsureExpressionType(CondExpr, boolType, ctx);
	}

	{
		ContextBlock contextBlock2;
		PreprocessContext::FlagsObj flagObj(ctx, PreprocessContext::FLAG_ENABLE_BREAK | PreprocessContext::FLAG_ENABLE_CONTINUE);
		PreprocessStatementNode(SubStatement, ctx);
	}

	if (!PostExpr.is_null())
	{
		Type voidType = Type(Type::TYPE_VOID);
		PreprocessExpressionNode(PostExpr, ctx, TypeSuggestion(voidType, 0));
		EnsureExpressionType(PostExpr, voidType, ctx);
	}

	return StatementPtr();
}

void ForStatement::Exec()
{
	ContextBlock contextBlock;

	if (!InitStatement.is_null())
		InitStatement->Exec();

	if (!CondExpr.is_null())
	{
		bool bval;
		for (;;)
		{
			CondExpr->Eval(&bval);
			if (!bval)
				break;
			
			try
			{
				ContextBlock contextBlock2;
				SubStatement->Exec();
			}
			catch (const BreakException&)
			{
				return;
			}
			catch (const ContinueException&)
			{
				// Go further.
			}

			if (!PostExpr.is_null())
				PostExpr->Eval(NULL);
		}
	}
	else
	{
		for (;;)
		{
			try
			{
				ContextBlock contextBlock;
				SubStatement->Exec();
			}
			catch (const BreakException&)
			{
				return;
			}
			catch (const ContinueException&)
			{
				// Go further.
			}

			if (!PostExpr.is_null())
				PostExpr->Eval(NULL);
		}
	}
}

ForStatement::~ForStatement()
{

}

StatementPtr BreakStatement::Preprocess( PreprocessContext &ctx )
{
	if (!ctx.CheckFlag(PreprocessContext::FLAG_ENABLE_BREAK))
		throw Error("Break not allowed here (must be inside a loop or switch)");
	return StatementPtr();
}

void BreakStatement::Exec()
{
	throw BreakException();
}

StatementPtr ContinueStatement::Preprocess( PreprocessContext &ctx )
{
	if (!ctx.CheckFlag(PreprocessContext::FLAG_ENABLE_CONTINUE))
		throw Error("Continue not allowed here (must be inside a loop)");
	return StatementPtr();
}

void ContinueStatement::Exec()
{
	throw ContinueException();
}

MemberOperator::~MemberOperator()
{

}

ExpressionPtr MemberOperator::Preprocess( PreprocessContext &ctx, const TypeSuggestion &typeSuggestion )
{
	PreprocessExpressionNode(SubExpr, ctx, TypeSuggestion());

	ExprType.Clear();
	if (SubExpr->ExprType == Type(Type::TYPE_STRING))
	{
		if (MemberName == "length")
			ExprType = Type(Type::TYPE_UINT32);
	}

	// Member not found
	if (ExprType.IsEmpty())
		throw Error("Member " + MemberName + " not found for type " + SubExpr->ExprType.GetName_r());

	return ExpressionPtr();
}

void MemberOperator::Eval( void *out )
{
	if (SubExpr->ExprType == Type(Type::TYPE_STRING))
	{
		string val;
		SubExpr->Eval(&val);
		if (MemberName == "length")
			*(uint*)out = (uint)val.length();
		else
			assert(0);
	}
	else
		assert(0);
}

IndexExpression::~IndexExpression()
{

}

ExpressionPtr IndexExpression::Preprocess( PreprocessContext &ctx, const TypeSuggestion &typeSuggestion )
{
	// TODO: maybe include typeSuggestion into finding correct overloaded operator[] version and so determining resulting ExprType?

	PreprocessExpressionNode(ThisExpr, ctx, TypeSuggestion());

	ExprType.Clear();
	if (ThisExpr->ExprType == Type(Type::TYPE_STRING))
	{
		if (IndexExpr.size() == 1)
		{
			Type indexType = Type(Type::TYPE_UINT32);
			PreprocessExpressionNode(IndexExpr[0], ctx, TypeSuggestion(indexType, 0));
			EnsureExpressionType(IndexExpr[0], indexType, ctx);
			ExprType = Type(Type::TYPE_CHAR);
		}
	}

	// Member not found
	if (ExprType.IsEmpty())
		throw Error("Invalid index for type " + ThisExpr->ExprType.GetName_r());

	return ExpressionPtr();
}

void IndexExpression::Eval( void *out )
{
	if (ThisExpr->ExprType == Type(Type::TYPE_STRING))
	{
		string thisVal;
		ThisExpr->Eval(&thisVal);
		uint index;
		IndexExpr[0]->Eval(&index);
		if (index < 0 || index >= thisVal.length())
			throw Error("Character index for string out of bounds");
		*(char*)out = thisVal[index];
	}
	else
		assert(0);
}

bool IndexExpression::IsLvalue()
{
	if (ThisExpr->ExprType == Type(Type::TYPE_STRING))
		return ThisExpr->IsLvalue();
	return false;
}

void * IndexExpression::AccessLvalue()
{
	if (ThisExpr->ExprType == Type(Type::TYPE_STRING))
	{
		string *thisLval = (string*)ThisExpr->AccessLvalue();
		int index;
		IndexExpr[0]->Eval(&index);
		if (index < 0 || index >= (int)thisLval->length())
			throw Error("String index out of bounds");
		return &(*thisLval)[(size_t)index];
	}
	else { assert(0); return NULL; }
}

SwitchStatement::~SwitchStatement()
{

}

StatementPtr SwitchStatement::Preprocess( PreprocessContext &ctx )
{
	PreprocessExpressionNode(ValExpr, ctx, TypeSuggestion()); // Suggest equatable some way>
	if (!ValExpr->ExprType.IsEquatable())
		throw Error("Type " + ValExpr->ExprType.GetName_r() + " is not equatable (type must be equatable to use it in switch)");

	PreprocessContext::FlagsObj flagsObj(ctx, PreprocessContext::FLAG_ENABLE_BREAK);
	assert(ValueExprs.size() == SubStatements.size());
	for (uint i = 0; i < ValueExprs.size(); i++)
	{
		PreprocessExpressionNode(ValueExprs[i], ctx, TypeSuggestion(ValExpr->ExprType, 0));
		EnsureExpressionType(ValueExprs[i], ValExpr->ExprType, ctx);

		if (!SubStatements[i].is_null())
			PreprocessStatementNode(SubStatements[i], ctx);
	}

	if (!DefaultStatement.is_null())
		PreprocessStatementNode(DefaultStatement, ctx);

	return StatementPtr();
}

void SwitchStatement::Exec()
{
	const Type &valType = ValExpr->ExprType;
	Value lhs(valType), rhs(valType);
	ValExpr->Eval(lhs.GetAddr());

	try
	{
		uint count = ValueExprs.size(), index;
		
		for (index = 0; index < count; index++)
		{
			ValueExprs[index]->Eval(rhs.GetAddr());
			if (valType.ValuesEqual(lhs.GetAddr(), rhs.GetAddr()))
				break;
		}
		
		for ( ; index < count; index++)
			if (!SubStatements[index].is_null())
				SubStatements[index]->Exec();

		if (!DefaultStatement.is_null())
			DefaultStatement->Exec();
	}
	catch (const BreakException&)
	{
		// Just return.
	}
}

FunctionParamDef::~FunctionParamDef()
{

}

void FunctionParamDef::Preprocess( PreprocessContext &ctx )
{
	if (!InitExpr.is_null())
		PreprocessExpressionNode(InitExpr, ctx, TypeSuggestion(ExprType, 0));

	// Automatic type inference
	if (ExprType == Type(Type::TYPE_AUTO))
	{
		if (!InitExpr.is_null())
			ExprType = InitExpr->ExprType;
		else
			throw Error("Cannot determine parameter type for " + Name);
	}

	// Ensure type is valid
	if (!ExprType.CheckClass(Type::CLASS_PHYSICAL))
		Error("Invalid type of parameter " + Name);

	// Check if variable can be added here, add it
	if (g_Context.VarMan.FindLocal(Name, NULL, NULL))
		throw Error(Name + " already defined in this scope");
	g_Context.VarMan.New(Name, ExprType, false);

	// Adjust initial value type for parameter type
	if (!InitExpr.is_null())
		EnsureExpressionType(InitExpr, ExprType, ctx);
}

/*void FunctionParamDef::Exec()
{
	void *addr = g_Context.VarMan.New(Name, ExprType, false);
	if (!InitExpr.is_null())
		InitExpr->Eval(addr);
	else
		InitValue(addr, ExprType);
}*/

void FunctionParamDef::CallGivenValue( void *addr )
{
	void *dstAddr = g_Context.VarMan.New(Name, ExprType, false);
	ExprType.CopyValue(dstAddr, addr);
}

void FunctionParamDef::CallDefaultValue()
{
	void *addr = g_Context.VarMan.New(Name, ExprType, false);
	assert(!InitExpr.is_null());
	InitExpr->Eval(addr);
}

FunctionDef::~FunctionDef()
{

}

void FunctionDef::Preprocess1_Symbols( PreprocessContext &ctx )
{
	// Check result type (real ones and void allowed)
	if (!ResultType.CheckClass(Type::CLASS_COMPILED))
		throw Error("You cannot use " + ResultType.GetName_r() + " as function result type");

	// Check parameters
	for (uint i = 0; i < ParamDefs.size(); i++)
	{
		// Check parameter type (only real ones allowed)
		if (!ParamDefs[i]->ExprType.CheckClass(Type::CLASS_PHYSICAL))
			throw Error("You cannot use type " + ParamDefs[i]->ExprType.GetName_r() + " as parameter type");
		// Check if parameter with no defult value is not after a parameter with default value
		if (i > 0 && ParamDefs[i]->InitExpr.is_null() && !ParamDefs[i-1]->InitExpr.is_null())
			throw Error("Parameters with default values must be at the end on the parameter list");
	}

	g_Context.FuncMan.Register(Name, RegisteredFunctionPtr(new RegisteredRegScriptFunction(this)));
}

void FunctionDef::Preprocess2_Content( PreprocessContext &ctx )
{
	ContextBlock contextBlock;
	ctx.ResultType = ResultType;
	
	for (uint i = 0; i < ParamDefs.size(); i++)
		ParamDefs[i]->Preprocess(ctx);
	PreprocessSubStatements(ctx);

	ctx.ResultType.Clear();
}

void FunctionDef::ExecStart()
{
	// Nothing.
}

void FunctionDef::Call( void *out, void **params, uint paramCount )
{
	ContextBlock ctxBlock;
	try
	{
		uint formalParamCount = ParamDefs.size();
		assert(paramCount <= formalParamCount);
		uint param_i;
		// Add given parameters
		for (param_i = 0; param_i < paramCount; param_i++)
			ParamDefs[param_i]->CallGivenValue(params[param_i]);
		// Add default parameters
		for ( ; param_i < formalParamCount; param_i++)
			ParamDefs[param_i]->CallDefaultValue();

		// Execute body
		ExecSubStatements();

		if (ResultType.CheckClass(Type::CLASS_PHYSICAL))
			throw Error("Function " + Name + " ended without returning value");
	}
	catch (ReturnException &returnException)
	{
		if (ResultType == Type(Type::TYPE_VOID))
		{
			assert(out == NULL && returnException.Val.is_null());
		}
		else
		{
			assert(!returnException.Val.is_null() && returnException.Val->GetType() == ResultType);
			ResultType.CopyValue(out, returnException.Val->GetAddr());
		}
	}
}

void Module::Preprocess()
{
	g_Context.StartPreprocessing();

	PreprocessContext ctx;
	
	ContextBlock ctxBlock;

	for (uint i = 0; i < Elements.size(); i++)
		Elements[i]->Preprocess1_Symbols(ctx);
	for (uint i = 0; i < Elements.size(); i++)
		Elements[i]->Preprocess2_Content(ctx);
}

void Module::ExecStart()
{
	// Register and init global variables, register functions
	for (uint i = 0; i < Elements.size(); i++)
		Elements[i]->ExecStart();
}

void Module::Exec()
{
	g_Context.StartWorking();

	ContextBlock ctxBlock;

	ExecStart();

	RegisteredFunction *func = NULL;
	FunctionManager::FIND_RESULT findResult = g_Context.FuncMan.FindExactMatch(func, "main", Type(Type::TYPE_VOID), NULL, 0);
	if (findResult != FunctionManager::FIND_RESULT_OK)
		throw Error("Function main not found");
	func->Exec(NULL, NULL, 0);
}

void GlobalVariableDef::Preprocess1_Symbols( PreprocessContext &ctx )
{
	for (uint i = 0; i < VariableDefs.size(); i++)
	{
		VariableDefs[i]->ValidateType();
		VariableDefs[i]->ValidateNameInContext();
		VariableDefs[i]->RegisterInContext();
	}
}

void GlobalVariableDef::Preprocess2_Content( PreprocessContext &ctx )
{
	for (uint i = 0; i < VariableDefs.size(); i++)
	{
		VariableDefs[i]->PreprocessInitialValue(ctx);
		VariableDefs[i]->AdjustInitialValueType(ctx);
	}
}

void GlobalVariableDef::ExecStart()
{
	for (uint i = 0; i < VariableDefs.size(); i++)
		VariableDefs[i]->Exec();
}

StatementPtr VariableDefinitionStatement::Preprocess( PreprocessContext &ctx )
{
	for (uint i = 0; i < VariableDefs.size(); i++)
	{
		VariableDefs[i]->PreprocessInitialValue(ctx);
		VariableDefs[i]->InferType();
		VariableDefs[i]->ValidateType();
		VariableDefs[i]->ValidateNameInContext();
		VariableDefs[i]->RegisterInContext();
		VariableDefs[i]->AdjustInitialValueType(ctx);
	}

	return StatementPtr();
}

void VariableDefinitionStatement::Exec()
{
	for (uint i = 0; i < VariableDefs.size(); i++)
		VariableDefs[i]->Exec();
}

StatementPtr ReturnStatement::Preprocess( PreprocessContext &ctx )
{
	assert(ctx.ResultType.CheckClass(Type::CLASS_COMPILED));
	// ResultType == void: ResultExpr can be NULL.
	if (ctx.ResultType != Type(Type::TYPE_VOID) && ResultExpr.is_null())
		throw Error("You must return value of type " + ctx.ResultType.GetName_r());
	if (!ResultExpr.is_null())
	{
		PreprocessExpressionNode(ResultExpr, ctx, TypeSuggestion(ctx.ResultType, 0));
		EnsureExpressionType(ResultExpr, ctx.ResultType, ctx);
	}

	return StatementPtr();
}

void ReturnStatement::Exec()
{
	if (ResultExpr.is_null())
		throw ReturnException();
	else if (ResultExpr->ExprType == Type(Type::TYPE_VOID))
	{
		ResultExpr->Eval(NULL);
		throw ReturnException();
	}
	else
	{
		ValuePtr val(new Value(ResultExpr->ExprType));
		ResultExpr->Eval(val->GetAddr());
		throw ReturnException(val);
	}
}

void CodeBlock::Preprocess()
{
	g_Context.StartPreprocessing();
	PreprocessContext ctx;
	ContextBlock ctxBlock;
	ctx.ResultType = Type(Type::TYPE_VOID);

	PreprocessSubStatements(ctx);

	ctx.ResultType.Clear();
}

void CodeBlock::Exec()
{
	g_Context.StartWorking();
	ContextBlock ctxBlock;
	try
	{
		ExecSubStatements();
	}
	catch (ReturnException &returnException)
	{
		assert(returnException.Val.is_null());
	}
}

template <typename T> void ExplicitNumericCodeConstant::InitValueAsReal_T(Value *val)
{
	if (!StrToSth((T*)val->GetAddr(), ValueStr))
		throw Error("Cannot parse real value");
}

template <typename T> void ExplicitNumericCodeConstant::InitValueAsInteger_T(Value *val)
{
	if (ValueStr.length() > 2 && ValueStr[0] == '0')
	{
		// I know T can be signed type here, but anyway the value is unsigned.
		if (ValueStr[1] == 'x' || ValueStr[1] == 'X')
		{
			if (StrToUint((T*)val->GetAddr(), ValueStr.substr(2), 16) != 0)
				throw Error("Cannot parse hexadecimal integer number");
			return;
		}
		else if (ValueStr[1] == 'b' || ValueStr[1] == 'B')
		{
			if (StrToUint((T*)val->GetAddr(), ValueStr.substr(2), 2) != 0)
				throw Error("Cannot parse binary integer number");
			return;
		}
	}

	if (!StrToSth((T*)val->GetAddr(), ValueStr))
		throw Error("Cannot parse decimal integer number");
}

ExpressionPtr ExplicitNumericCodeConstant::Preprocess( PreprocessContext &ctx, const TypeSuggestion &typeSuggestion )
{
	ValuePtr val;

	uint cls = typeSuggestion.Class;

	// Integer or real?

	bool canBeInteger = ConstType == CONST_TYPE_DEC
		|| ConstType == CONST_TYPE_BIN
		|| ConstType == CONST_TYPE_HEX;
	bool canBeReal = ConstType == CONST_TYPE_DEC
		|| ConstType == CONST_TYPE_REAL;

	// Both possible
	if (canBeInteger && canBeReal)
	{
		bool intSuggested  = (cls & Type::CLASS_INTEGER) || typeSuggestion.T.CheckClass(Type::CLASS_INTEGER);
		bool realSuggested = (cls & Type::CLASS_REAL)    || typeSuggestion.T.CheckClass(Type::CLASS_REAL);
		// Both suggested or none suggested
		if (intSuggested == realSuggested)
			// Prefer integer
			cls = cls & ~Type::CLASS_REAL | Type::CLASS_INTEGER;
		// One of them suggested
		else if (intSuggested)
			cls = cls & ~Type::CLASS_REAL | Type::CLASS_INTEGER;
		else // realSuggested
			cls = cls & ~Type::CLASS_INTEGER | Type::CLASS_REAL;
	}
	else if (canBeInteger)
		cls = cls & ~Type::CLASS_REAL | Type::CLASS_INTEGER;
	else // canBeReal
		cls = cls & ~Type::CLASS_INTEGER | Type::CLASS_REAL;

	if (cls & Type::CLASS_REAL)
	{
		// Suggested float
		if (typeSuggestion.T.GetType() == Type::TYPE_FLOAT)
		{
			val.reset(new Value(Type::TYPE_FLOAT));
			InitValueAsReal_T<float>(val.get());
		}
		// Preferred double
		else
		{
			val.reset(new Value(Type::TYPE_DOUBLE));
			InitValueAsReal_T<double>(val.get());
		}
	}
	else // CLASS_INTEGER
	{
		// Suggested signed
		if ((cls & Type::CLASS_SIGNED) || typeSuggestion.T.CheckClass(Type::CLASS_SIGNED))
		{
			if (typeSuggestion.T.GetType() == Type::TYPE_INT8)
			{
				val.reset(new Value(Type::TYPE_INT8));
				InitValueAsInteger_T<int1>(val.get());
			}
			else if (typeSuggestion.T.GetType() == Type::TYPE_INT16)
			{
				val.reset(new Value(Type::TYPE_INT16));
				InitValueAsInteger_T<int2>(val.get());
			}
			else if (typeSuggestion.T.GetType() == Type::TYPE_INT32)
			{
				val.reset(new Value(Type::TYPE_INT32));
				InitValueAsInteger_T<int4>(val.get());
			}
			else
			{
				val.reset(new Value(Type::TYPE_INT64));
				InitValueAsInteger_T<int8>(val.get());
			}
		}
		// Preferred unsigned
		else
		{
			if (typeSuggestion.T.GetType() == Type::TYPE_UINT8)
			{
				val.reset(new Value(Type::TYPE_UINT8));
				InitValueAsInteger_T<uint1>(val.get());
			}
			else if (typeSuggestion.T.GetType() == Type::TYPE_UINT16)
			{
				val.reset(new Value(Type::TYPE_UINT16));
				InitValueAsInteger_T<uint2>(val.get());
			}
			else if (typeSuggestion.T.GetType() == Type::TYPE_UINT32)
			{
				val.reset(new Value(Type::TYPE_UINT32));
				InitValueAsInteger_T<uint4>(val.get());
			}
			else
			{
				val.reset(new Value(Type::TYPE_UINT64));
				InitValueAsInteger_T<uint8>(val.get());
			}
		}
	}

	return ExpressionPtr(new Constant(val, true));
}

void ExplicitNumericCodeConstant::RecognizeIntType()
{
	if (StrBeginsI(ValueStr, "0x"))
		ConstType = CONST_TYPE_HEX;
	else if (StrBeginsI(ValueStr, "0b"))
		ConstType = CONST_TYPE_BIN;
	else
		ConstType = CONST_TYPE_DEC;
}
